package com.self.ry.config.redis;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Copyright: Copyright (c) 2019
 *
 * @Description: 该类的功能描述
 * @Version: v1.0.0
 * @Author ruyi
 * @Date 2019/6/27 19:55
 */
@Configuration
public class RedisConfig {

    /**
     * 创建 RedisTemplate
     * @param factory 连接工厂
     * @return
     */
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate redisTemplate = new RedisTemplate<>();
        // 设定 连接工厂
        redisTemplate.setConnectionFactory(factory);
        // 设定 KEY的序列化为String
        redisTemplate.setKeySerializer(new StringRedisSerializer());

        setValueSerializer(redisTemplate);
        redisTemplate.afterPropertiesSet();

        // 开启事务，默认不支持的
        // 如果设置为true，RedisTemplate将使用MULTI ... EXEC | DISCARD来跟踪操作。
        // 这样是强制将当前RedisConnection 绑定到触发MULTI的当前线程上。若事务完成且没有错误则调用EXEC
        // 否则调用DISCARD。
        // 进入MULTI后，RedisConnection会对写入操作进行排队。
        // 所有只读操作（如KEYS）都通过管道传输到一个新的（非线程绑定的）RedisConnection。
        // 事务管理平台使用 PlatformTransactionManager
        //redisTemplate.setEnableTransactionSupport(true);
        return redisTemplate;
    }


    /**
     * 使用Jackson序列化Value值
     * @param template
     */
    private void setValueSerializer(RedisTemplate template) {
        // 此转换器可用于绑定到类型化的bean或无类型的HashMap实例。
        // 注意：空对象被序列化为空数组，反之亦然
        Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        // 可序列化所有标准的JDK类型
        //ObjectMapper提供了从基本POJO（普通旧Java对象）或从通用JSON树模型JsonNode读取和写入JSON的功能，
        // 以及用于执行转换的相关功能。 它还具有高度可定制性，可以使用不同样式的JSON内容，
        // 并支持更高级的Object概念，如多态和对象标识。
        ObjectMapper om = new ObjectMapper();
        // 用于更改此对象映射器的打开/关闭反序列化功能的状态的方法。
        // 确定是否可以强制使用非数组（在JSON中）值以使用Java集合（数组，java.util.Collection）类型的功能。
        // 如果启用，集合反序列化器将尝试处理非数组值，就像它们具有围绕JSON数组的“隐式”一样。
        om.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        // 配置为false表示mapper在遇到mapper对象中存在json对象中没有的数据变量时不报错，可以进行反序列化
        // 在遇到未知属性的时候不抛出异常
        // 确定是否遇到未知属性（不映射到属性的属性，并且没有“任何setter”或处理程序可以处理该属性）的功能应导致失败（通过引发JSonMappingException）。
        // 此设置仅在尝试了未知属性的所有其他处理方法并且属性未处理后生效。
        om.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        // 用于确定是否可以使用常规的“getter”方法（但只能使用处理集合和Maps的方法，而不能使用其他类型的getter）
        // 来获取对集合和Map的引用以修改属性，而不需要setter方法。
        om.disable(MapperFeature.USE_GETTERS_AS_SETTERS);

        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
        om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 美化输出
        om.enable(SerializationFeature.INDENT_OUTPUT);
        // 允许序列化空的POJO类 （否则会抛出异常）
        om.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);

        jsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jsonRedisSerializer);
    }

}
